#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "lua.h"
#include "lauxlib.h"

// redis相关
#include "../fmacros.h"
#include "../config.h"
#include "../dict.h"
#include "../sds.h"
#include "../zmalloc.h"

static dict * myNumDefineDict = NULL;
static dict * myIntDefineDict = NULL;
static dict * myStrDefineDict = NULL;

uint64_t hashCallback(const void *key) {
    return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
}

int compareCallback(void *privdata, const void *key1, const void *key2) {
    int l1,l2;
    DICT_NOTUSED(privdata);

    l1 = sdslen((sds)key1);
    l2 = sdslen((sds)key2);
    if (l1 != l2) return 0;
    return memcmp(key1, key2, l1) == 0;
}

void freeCallback(void *privdata, void *val) {
    DICT_NOTUSED(privdata);

    sdsfree(val);
}

static dictType num_hash_type = {
    hashCallback,                   /* hash function */
    NULL,                          /* key dup */
    NULL,                          /* val dup */
    compareCallback,               /* key compare */
    freeCallback,                  /* key destructor */
    NULL                           /* val destructor */
};

static dictType str_hash_type = {
    hashCallback,                   /* hash function */
    NULL,                          /* key dup */
    NULL,                          /* val dup */
    compareCallback,               /* key compare */
    freeCallback,                  /* key destructor */
    freeCallback                   /* val destructor */
};

static int lua_f_addtab( lua_State *L )
{
    int index = lua_gettop(L);
    lua_pushnil(L);  /* first key */
     while (lua_next(L, index) != 0) {
       /* 'key' is at index -2 and 'value' at index -1 */
       int keylen = 0;
       int valuelen = 0;
       if (lua_type(L, -2) == LUA_TSTRING ){
            const char* key = lua_tostring(L, -2);
            sds realkey = sdsnew(key);
            if (lua_type(L, -1) == LUA_TSTRING)
            {
                const char* value = lua_tostring(L, -1);
                printf("index =%d,key value=>%s - %s\n",index, key, value);
                sds realvalue = sdsnew(value);
                dictAdd(myStrDefineDict,realkey,realvalue);
            }else if (lua_type(L, -1) == LUA_TNUMBER)
            {
                if (lua_isinteger(L, -1)) {
                    lua_Integer num = lua_tointeger(L, -1);
                    dictEntry *entry = dictAddRaw(myIntDefineDict,realkey,NULL);
                    if(entry)
                    {
                        dictSetSignedIntegerVal(entry,num);
                    }
                }else
                {
                    double num = lua_tonumber(L, -1);
                    dictEntry *entry = dictAddRaw(myNumDefineDict,realkey,NULL);
                    if(entry)
                    {
                        dictSetDoubleVal(entry,num);
                    }
                }
            }
            
       }else
       {
            printf("%s - %s\n",
                lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));
       }
       
       lua_pop(L, 1);  /* removes 'value'; keeps 'key' for next iteration */
    }

    return 0;
}

static int lua_f_addstr( lua_State *L )
{
    const char* key = lua_tostring(L, 1);
    const char* value = lua_tostring(L, 2);

    sds realkey = sdsnew(key);
    sds realvalue = sdsnew(value);
    dictAdd(myStrDefineDict,realkey,realvalue);

    lua_pushinteger(L,1);
    return 1;
}

static int lua_f_getstr( lua_State *L )
{
    const char* key = lua_tostring(L, 1);
    sds realkey = sdsnew(key);
    dictEntry * he = dictFind(myStrDefineDict, realkey);
    sdsfree(realkey);
    if( NULL == he)
    {
        printf("get str error for key = %s \n",key);
        lua_pushnil(L);
        return 1;
    }

    void * value = dictGetVal(he);
    if(NULL == value)
    {
        lua_pushnil(L);
        return 1;
    }
    sds realvalue = (sds) value;
    int reallen = sdslen(realvalue);
    lua_pushlstring(L,realvalue,reallen);
    return 1;
}

static int lua_f_addnum( lua_State *L )
{
    const char* key = lua_tostring(L, 1);
    short success = 0;
    sds realkey = sdsnew(key);
    if (lua_isinteger(L, 2)) {
        lua_Integer num = lua_tointeger(L, 2);
        dictAdd(myIntDefineDict,realkey,num);
        dictEntry *entry = dictAddRaw(myIntDefineDict,realkey,NULL);
        if(entry)
        {
            dictSetSignedIntegerVal(entry,num);
            success = 1;
        }
    }else
    {
        double num = lua_tonumber(L, 2);
        dictEntry *entry = dictAddRaw(myNumDefineDict,realkey,NULL);
        if(entry)
        {
            dictSetDoubleVal(entry,num);
            success = 1;
        }
    }
    lua_pushinteger(L,success);
    return 1;
}

static int lua_f_getint( lua_State *L )
{
    const char* key = lua_tostring(L, 1);
    sds realkey = sdsnew(key);
    dictEntry * he = dictFind(myIntDefineDict, realkey);
    sdsfree(realkey);
	if( NULL == he)
	{
		lua_pushnil(L);
		return 1;
	}
	int value = dictGetSignedIntegerVal(he);
    lua_pushinteger(L,value);
    return 1;
}

static int lua_f_getnum( lua_State *L )
{
    const char* key = lua_tostring(L, 1);
    sds realkey = sdsnew(key);
    dictEntry * he = dictFind(myNumDefineDict, realkey);
    sdsfree(realkey);
    if( NULL == he)
    {
        lua_pushnil(L);
        return 1;
    }
    double value = dictGetDoubleVal(he);
    lua_pushnumber(L,value);
    return 1;
}

static int lua_f_release( lua_State *L )
{
	dictRelease(myNumDefineDict);
	myNumDefineDict = NULL;

    dictRelease(myStrDefineDict);
    myStrDefineDict = NULL;

    dictRelease(myIntDefineDict);
    myIntDefineDict = NULL;
    
	return 0;
}

LUALIB_API int 
luaopen_resharedDefine( lua_State *L )
{
    luaL_checkversion(L);

    luaL_Reg l[] = {
        {"addtab",lua_f_addtab},
        {"addstr",lua_f_addstr},
        {"addnum",lua_f_addnum},
        {"getstr",lua_f_getstr},
        {"getnum",lua_f_getnum},
        {"getint",lua_f_getint},
        {"release",lua_f_release},
        {NULL, NULL}
    };
    luaL_newlib(L,l);
	
    if( NULL == myNumDefineDict)
        myNumDefineDict = dictCreate(&num_hash_type, NULL);

    if( NULL == myStrDefineDict)
        myStrDefineDict = dictCreate(&str_hash_type, NULL);

    if( NULL == myIntDefineDict)
        myIntDefineDict = dictCreate(&num_hash_type, NULL);

    return 1;
}
